import sys
import re
import subprocess

__example_match__ = '''protocol.h(30) : Typemap for ROLE_INFO info_list[10] (memberin) : %typemap(memberin) SWIGTYPE [ANY]'''
__example_char__ = '''protocol.h(30) : Typemap for char info_list[10] (memberin) : %typemap(memberin) SWIGTYPE [ANY]'''
__example_unsiged_char__ = '''protocol.h(30) : Typemap for unsigned char info_list[10] (memberin) : %typemap(memberin) SWIGTYPE [ANY]'''

__swig_command__ = 'swig -c++ -python -py3 -modern -O -debug-tmused'

class RedirectStdoutTo:
    def __init__(self, out_new):
        self.out_new = out_new

    def __enter__(self):
        self.out_old = sys.stdout
        sys.stdout = self.out_new

    def __exit__(self, *args):   
        sys.stdout = self.out_old
        
def TrueMatch(text):
    return text, True, True, True

def NoneMatch(text):
    return text, None, None, None

def MatchAny():
    def DoFilte(obj):
        return obj
    return DoFilte

def MatchMemberIn(filter_func):
    def DoFilte(obj):
        text, _type_, name, typemap = filter_func(obj)
        result = re.search('''.* : Typemap for (.+) ([a-zA-Z_]\w+\[.*\]) \(memberin\) : %typemap\(memberin\) (SWIGTYPE \[.*\])$''', text)
        if result:
            _type_, name, typemap = result.group(1, 2, 3)
            return text, _type_, name, typemap
        else:
            return NoneMatch(text)
    return DoFilte
    
def FilterCharType(filter_func):
    def DoFilte(obj):
        text, _type_, name, typemap = filter_func(obj)
        if _type_ and 'char' not in _type_.split(' '):
            return text, _type_, name, typemap
        else:
            return NoneMatch(text)
    return DoFilte

def MatchedLines(src, match_func):
    for line in src:
        obj = match_func(TrueMatch(line))
        if obj != NoneMatch(line):
            yield obj
            
def ArrayClass(type_table):
    return ['%array_functions({0}, {1})'.format(v, v + '_Array') for v in type_table]

def CData(type_table):
    return ['%cdata({0})'.format(v) for v in type_table]

def OutPut(type_table, out_file):
    with RedirectStdoutTo(out_file):
        print(
'''/* Automatically generated by {0}, DO NOT edit by hand. */
%include carrays.i
%include cdata.i
'''.format(__file__))
        print('%pybuffer_mutable_string(char *outMemBuf);')
        print('%pybuffer_string(const char *inMemBuf);')
        
        for item in type_table:
            joined_name = '_'.join(item.split(' '))
            print('%array_functions({0}, {1})'.format(item, joined_name + '_Array'))
            print('')
            print('%inline %{')
            print('')
            print('/* {0} */'.format(item))
            print('    size_t sizeof_{0}() {{ return sizeof({1}); }}'.format(joined_name, item))
            print('')
            print('    PyObject* {0}_ArrayFromBuffer({1} *outArrayBuf, int outArrayItemNumber,'.format(joined_name, item))
            print('                                  const char *inMemBuf, int inMemBufSize)')
            print('    {')
            print('        if (outArrayItemNumber * sizeof({0}) >= inMemBufSize) {{'.format(item))
            print('            memcpy(outArrayBuf, inMemBuf, inMemBufSize);')
            print('            return PyInt_FromLong(inMemBufSize);')
            print('        }')
            print('        else {')
            print('            return Py_None;')
            print('        }')
            print('     }')
            print('')
            print('     PyObject* {0}_ArrayToBuffer(char *outMemBuf, int outMemBufSize,'.format(joined_name))
            print('                                 {0} *inArrayBuf, int inArrayItemNumber)'.format(item))
            print('     {')
            print('         size_t in_array_size = sizeof({0}) * inArrayItemNumber;'.format(item))
            print('         if (outMemBufSize >= in_array_size) {')
            print('             memcpy(outMemBuf, inArrayBuf, in_array_size);')
            print('             return PyInt_FromLong(in_array_size);')
            print('         }')
            print('         else {')
            print('             return Py_None;')
            print('         }')
            print('      }')
            print('%}')
            print('')

        print('''/* {0} Ends here. */'''.format(out_file.name))

def SwigParseTypemap(src_file):
    tmp_file = '{0}_tmused'.format(src_file)
    swig_proc = None
    with open(tmp_file, 'w') as swig_out:
        swig_proc = subprocess.Popen('{0} {1}'.format(__swig_command__, src_file).split(),
                                     stdout = swig_out)
    # Join with swig process
    swig_proc.communicate()
    return tmp_file

def Parse(src_file, dest_file=None):
    swig_out_file = SwigParseTypemap(src_file)

    type_table = {}
    match_func = MatchMemberIn(MatchAny())
    #match_func = FilterCharType(MatchMemberIn(MatchAny()))
    # parse swig output file
    with open(swig_out_file) as swig_out:
        type_table = {item[1] for item in MatchedLines(swig_out, match_func)}
    
    try:
        with open(dest_file, mode='w', encoding='utf-8') as dest:
            OutPut(type_table, dest)
    except:
        OutPut(type_table, sys.stdout)

def Usage():
    print('Usage: parse_array.py <input> [<output>]')

if __name__ == '__main__':
    if len(sys.argv) < 2:
        Usage()
    else:
        Parse(*sys.argv[1:])
